package com.tim.util;

import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 敏感词校验
 *  初始化敏感词库，将敏感词加入到HashMap中，构建DFA算法模型
 */
@Slf4j
public class SensitiveWordUtil {

    /**
     * 加载敏感词库。
     *  存在数据库、redis；取出后构建检索字典树
     * @return
     */
    public Map loadSensitiveWordMapTree(){
        Set<String> keyWordSet = null;
        try {
            String libStringCache = "";
//            从redis、数据库取#号分隔的敏感词配置
//            libStringCache = redisTemplate.get(key);
//            if(StringUtils.isBlank(libStringCache)){
//                String libString = sensitiveWordLibMapper.getSensitiveWordLibByType(PlatFormSensitiveWordEnum.APP_TYPE.getCode());
//                redisTemplate.set(key, libString);
//                redisTemplate.expire(key, PlatformRedisConstant.SENSITIVE_WORD_LIB_EXPIRE_TIME);
//            }
            keyWordSet = parseSensitiveWord(libStringCache);
            //将敏感词库加入到HashMap中
           Map sensitiveWordMap = addSensitiveWordToHashMap(keyWordSet);
           return sensitiveWordMap;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return new HashMap(10);
    }

    public Set<String> parseSensitiveWord(String libString){
        if(org.apache.commons.lang3.StringUtils.isNotBlank(libString)){
            String[] wordArray = libString.split("#");
            Set<String> keyWordSet = new HashSet<>(Arrays.asList(wordArray));
            return keyWordSet;
        }
        return new HashSet<>();
    }

    public static void main(String[] args) {
        String ss = "A#B#";
        ss = ss.substring(0, ss.length()-1);
        System.out.println(ss);
//        String ss = "#媚外#和弦#兼职#铃声#性伴侣#男公关#火辣#精子#射精#诱奸#步枪#气枪#电狗#手狗#枪模#模型枪#仿真枪#狙击枪#信号枪#麻醉枪#来福枪#来复枪#冲锋枪#散弹枪#卡宾枪#枪支#枪械#炸药#硝铵#火药#燃烧瓶#可燃物#爆炸物#炸弹#雷管#原子";
//        SensitiveWordServiceImpl sensitiveWordService = new SensitiveWordServiceImpl();
//        Set<String> setWord = sensitiveWordService.parseSensitiveWord(ss);
//        Map map = sensitiveWordService.addSensitiveWordToHashMap(setWord);
//        String string = "太多习近平的伤感情怀也许只局限于饲养基地日本人 荧幕中的情节，主人公尝试着去用某种方式渐渐的很潇洒地释自杀指南怀那些自己经历的伤感。"
//                + "然后法轮功 我们的扮演的角三唑仑色就是狗比跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中，然后感动就流泪，"
//                + "难过就躺在某一个人的怀里尽情的阐述心扉或者手机卡复制袭警器一个人一杯红酒一部电影在夜三级 深人静的晚上，关上电话静静的发呆着。";
//
//        SensitivewordFilterUtil filter = new SensitivewordFilterUtil();
//        filter.loadSensitivewordLibrary(map);
//        Set<String> set = filter.getSensitiveWord(string, 1);
//        System.out.println("语句中包含敏感词的个数为：" + set.size() + "。包含：" + set);
    }

    /**
     * 读取敏感词库，将敏感词放入HashSet中，构建一个DFA算法模型：<br>
     * 中 = {
     *      isEnd = 0
     *      国 = {<br>
     *      	 isEnd = 1
     *           人 = {isEnd = 0
     *                民 = {isEnd = 1}
     *                }
     *           男  = {
     *           	   isEnd = 0
     *           		人 = {
     *           			 isEnd = 1
     *           			}
     *           	}
     *           }
     *      }
     *  五 = {
     *      isEnd = 0
     *      星 = {
     *      	isEnd = 0
     *      	红 = {
     *              isEnd = 0
     *              旗 = {
     *                   isEnd = 1
     *                  }
     *              }
     *      	}
     *      }
     */
    private Map addSensitiveWordToHashMap(Set<String> keyWordSet) {
        HashMap sensitiveWordMap = new HashMap(keyWordSet.size());     //初始化敏感词容器，减少扩容操作
        String key = null;
        Map nowMap = null;
        Map<String, String> newWorMap = null;
        //迭代keyWordSet
        Iterator<String> iterator = keyWordSet.iterator();
        while(iterator.hasNext()){
            key = iterator.next();    //关键字
            nowMap = sensitiveWordMap;
            for(int i = 0 ; i < key.length() ; i++){
                char keyChar = key.charAt(i);       //转换成char型
                Object wordMap = nowMap.get(keyChar);       //获取

                if(wordMap != null){        //如果存在该key，直接赋值
                    nowMap = (Map) wordMap;
                }
                else{     //不存在则，则构建一个map，同时将isEnd设置为0，因为他不是最后一个
                    newWorMap = new HashMap<String,String>();
                    newWorMap.put("isEnd", "0");     //不是最后一个
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                if(i == key.length() - 1){
                    nowMap.put("isEnd", "1");    //最后一个
                }
            }
        }
        return sensitiveWordMap;
    }

    public List<String> checkSensitiveWord(String text, Boolean justReturn) {
        if(org.apache.commons.lang3.StringUtils.isNotBlank(text)){
            SensitivewordFilterUtil filter = new SensitivewordFilterUtil();
            Map map = this.loadSensitiveWordMapTree();
            filter.loadSensitivewordLibrary(map);
            Set<String> set = filter.getSensitiveWord(text, 1);
            log.info("语句中包含敏感词的个数为:{}, 敏感词: {}", set.size(), set);
            if(!justReturn && !set.isEmpty()){
                List<String> words = new ArrayList<>(set);
                String sensitiveWord = "";
                for(String s : words){
                    sensitiveWord+=s+"#";
                }

                if(org.apache.commons.lang3.StringUtils.isNotBlank(sensitiveWord)){
                    sensitiveWord = sensitiveWord.substring(0, sensitiveWord.length()-1);
                }

                throw new RuntimeException(sensitiveWord + " 为敏感词汇，请修改后重试");
            }
            return new ArrayList<>(set);
        }
        return new ArrayList<>();
    }


}

class SensitivewordFilterUtil {

    private Map sensitiveWordMap = null;
    public static int minMatchTYpe = 1;      //最小匹配规则
    public static int maxMatchType = 2;      //最大匹配规则

    /**
     * 加载敏感词库数据
     */
    public void loadSensitivewordLibrary(Map data){
        sensitiveWordMap = data;
    }

    /**
     * 判断文字是否包含敏感字符
     * @param txt  文字
     * @param matchType  匹配规则&nbsp;1：最小匹配规则，2：最大匹配规则
     * @return 若包含返回true，否则返回false
     */
    public boolean isContaintSensitiveWord(String txt,int matchType){
        boolean flag = false;
        for(int i = 0 ; i < txt.length() ; i++){
            int matchFlag = this.CheckSensitiveWord(txt, i, matchType); //判断是否包含敏感字符
            if(matchFlag > 0){    //大于0存在，返回true
                flag = true;
            }
        }
        return flag;
    }

    /**
     * 获取文字中的敏感词
     * @param txt 文字
     * @param matchType 匹配规则&nbsp;1：最小匹配规则，2：最大匹配规则
     */
    public Set<String> getSensitiveWord(String txt , int matchType){
        Set<String> sensitiveWordList = new HashSet<String>();

        for(int i = 0 ; i < txt.length() ; i++){
            int length = CheckSensitiveWord(txt, i, matchType);    //判断是否包含敏感字符
            if(length > 0){    //存在,加入list中
                sensitiveWordList.add(txt.substring(i, i+length));
                i = i + length - 1;    //减1的原因，是因为for会自增
            }
        }

        return sensitiveWordList;
    }

    /**
     * 替换敏感字字符
     * @param txt
     * @param matchType
     * @param replaceChar 替换字符，默认*
     */
    public String replaceSensitiveWord(String txt,int matchType,String replaceChar){
        String resultTxt = txt;
        Set<String> set = getSensitiveWord(txt, matchType);     //获取所有的敏感词
        Iterator<String> iterator = set.iterator();
        String word = null;
        String replaceString = null;
        while (iterator.hasNext()) {
            word = iterator.next();
            replaceString = getReplaceChars(replaceChar, word.length());
            resultTxt = resultTxt.replaceAll(word, replaceString);
        }

        return resultTxt;
    }

    /**
     * 获取替换字符串
     * @param replaceChar
     * @param length
     */
    private String getReplaceChars(String replaceChar,int length){
        String resultReplace = replaceChar;
        for(int i = 1 ; i < length ; i++){
            resultReplace += replaceChar;
        }
        return resultReplace;
    }

    /**
     * 检查文字中是否包含敏感字符，检查规则如下：<br>
     * @param txt
     * @param beginIndex
     * @param matchType
     * @return，如果存在，则返回敏感词字符的长度，不存在返回0
     */
    @SuppressWarnings({ "rawtypes"})
    public int CheckSensitiveWord(String txt,int beginIndex,int matchType){
        boolean  flag = false;    //敏感词结束标识位：用于敏感词只有1位的情况
        int matchFlag = 0;     //匹配标识数默认为0
        char word = 0;
        Map nowMap = sensitiveWordMap;
        for(int i = beginIndex; i < txt.length() ; i++){
            word = txt.charAt(i);
            nowMap = (Map) nowMap.get(word);     //获取指定key
            if(nowMap != null){     //存在，则判断是否为最后一个
                matchFlag++;     //找到相应key，匹配标识+1
                if("1".equals(nowMap.get("isEnd"))){       //如果为最后一个匹配规则,结束循环，返回匹配标识数
                    flag = true;       //结束标志位为true
                    if(SensitivewordFilterUtil.minMatchTYpe == matchType){    //最小规则，直接返回,最大规则还需继续查找
                        break;
                    }
                }
            }
            else{     //不存在，直接返回
                break;
            }
        }
        if(matchFlag < 2 || !flag){        //长度必须大于等于1，为词
            matchFlag = 0;
        }
        return matchFlag;
    }

    public static void main(String[] args) {
        SensitivewordFilterUtil filter = new SensitivewordFilterUtil();
        System.out.println("敏感词的数量：" + filter.sensitiveWordMap.size());
        String string = "太多的伤感情怀也许只局限于饲养基地日本人 荧幕中的情节，主人公尝试着去用某种方式渐渐的很潇洒地释自杀指南怀那些自己经历的伤感。"
                + "然后法轮功 我们的扮演的角色就是跟随着主人公的喜红客联盟 怒哀乐而过于牵强的把自己的情感也附加于银幕情节中，然后感动就流泪，"
                + "难过就躺在某一个人的怀里尽情的阐述心扉或者手机卡复制器一个人一杯红酒一部电影在夜三级片 深人静的晚上，关上电话静静的发呆着。";
        System.out.println("待检测语句字数：" + string.length());
        long beginTime = System.currentTimeMillis();
        Set<String> set = filter.getSensitiveWord(string, 1);
        long endTime = System.currentTimeMillis();
        System.out.println("语句中包含敏感词的个数为：" + set.size() + "。包含：" + set);
        System.out.println("总共消耗时间为：" + (endTime - beginTime));
    }
}